﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//6.野指针 - 指指针指向的地址不可知（随机，不存在，不正确）

//野指针出现的原因：

//6.1指针未初始化
int main()
{
	int* p; //未初始化，默认随机值
	*p = 20;
	return 0;
}


//6.2指针越界访问
int main()
{
	int arr[5] = {0};
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i <= 6; i++)
	{
		//当p指向超出数组arr的范围时就成为野指针
		*(p++) = i;
	}
	return 0;
}



//6.3访问的空间被释放
int* text()
{
	int n = 1000;
	return &n;
}
//该处的n变量是临时变量，在离开函数text时就会被销毁。
int main()
{
	int* p = text();
	printf("%p", *p);
	return 0;
}


//7.野指针的规避

//我们如果不知道创建的指针该指向哪里，或者暂时用不上时，我们可以给
//指针赋值NULL，这是C语言中定义的一个标识符常量，值是0，0也是地址，但是无法使用
//读写该地址会报错


//代码初始化如下：
int main()
{
	int num = 10;
	int* p1 = &num;
	int* p2 = NULL;
	return 0;
}


//指针在不使用时我们需要注意及时的重置为NULL，在使用前也要检查他的
//有效性
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	for (int i = 0; i < 10; i++)
	{
		*(p++) = i;
	}

	//运行完这个代码时p已经越界了，如果要再次使用需要重置为NULL

	p = NULL;
	p = &arr[0];
	if (p != NULL)
	{
		printf("err\n");
	}
	return 0;
}


//8.assert断言

//宏assert（），使用前需要包含头文件assert.h
//用于运行时确保程序符合指定的条件，如果不符合会进行断言，报错终止运行

//assert(p != NULL);

//上⾯代码在程序运⾏到这⼀⾏语句时，验证变量 p 是否等于 NULL 。
// 如果确实不等于 NULL ，程序继续运⾏，否则就会终⽌运⾏，
// 并且给出报错信息提⽰


//assert() 宏接受⼀个表达式作为参数。如果该表达式为真（返回值⾮零）， 
// assert() 不会产⽣任何作⽤，程序继续运⾏。
// 如果该表达式为假（返回值为零）， assert() 就会报错，在标准错误
//流 stderr 中写⼊⼀条错误信息，
// 显⽰没有通过的表达式，以及包含这个表达式的⽂件名和⾏号。


//如果不想使用了，在头文件前定义一个NDEBUG就可以取消了

//#define NDEBUG
//#include <assert.h>

//在Release版本中会选择禁用assert就行了



//9.指针的使用和传址调用

//9.1 strlen函数的实现
int my_strlen(char* s)
{
	int count = 0;
	while (*s)
	{
		count++;
		s++;
	}
	return count;
}


int main()
{
	int len = my_strlen("abcdef");
	printf("%d\n", len);
	return 0;
}


//9.2传值调用和传址调用


//传值调用
void Swap1(int a, int b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("交换前：%d %d", a, b);
	Swap1(a, b);
	printf("交换后：%d %d", a, b);
}


//传址调用
void Swap2(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("交换前：%d %d", a, b);
	Swap1(a, b);
	printf("交换后：%d %d", a, b);
}